package com.newtouch.bxzs.business.message.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.newtouch.bxzs.business.enclosure.mapper.AttachmentMapper;
import com.newtouch.bxzs.business.message.controller.NotificationController;
import com.newtouch.bxzs.business.message.mapper.NotificationMapper;
import com.newtouch.bxzs.business.message.mapper.NotificationUserMapMapper;
import com.newtouch.bxzs.business.message.model.NotificationBO;
import com.newtouch.bxzs.business.message.model.NotificationVO;
import com.newtouch.bxzs.business.message.model.bo.*;
import com.newtouch.bxzs.business.message.model.vo.ChangeNotificationStatusVO;
import com.newtouch.bxzs.business.message.model.vo.GetNotificationListVO;
import com.newtouch.bxzs.business.message.model.vo.GetServiceNotificationListVO;
import com.newtouch.bxzs.business.message.service.NotificationService;
import com.newtouch.bxzs.business.product.mapper.UserMapper;
import com.newtouch.bxzs.common.base.BaseErrorEnum;
import com.newtouch.bxzs.common.base.BaseException;
import com.newtouch.bxzs.common.base.constant.RabbitMQConstant;
import com.newtouch.bxzs.common.base.em.BusinessMessageEnum;
import com.newtouch.bxzs.common.base.em.ButtonTypeEnum;
import com.newtouch.bxzs.common.base.em.NotificationTypeEnum;
import com.newtouch.bxzs.common.base.em.PublishStatusEnum;
import com.newtouch.bxzs.common.base.model.Attachment;
import com.newtouch.bxzs.common.base.model.Notification;
import com.newtouch.bxzs.common.base.model.NotificationUserMap;
import com.newtouch.bxzs.common.base.model.bo.PushMessageBO;
import com.newtouch.bxzs.common.base.util.Assert;
import com.newtouch.bxzs.common.base.util.UserDetailUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.*;

import static com.alibaba.fastjson.JSON.parseArray;
import static com.alibaba.fastjson.JSON.toJSONString;

/**
 * 通知信息接口实现类
 *
 * <p>
 * <b>History:</b>
 * <table border="1">
 * <tr>
 * <th>Date</th>
 * <th>Operator</th>
 * <th>Memo</th>
 * </tr>
 * <tr>
 * <td>2020/12/22</td>
 * <td>liman</td>
 * <td>Create</td>
 * </tr>
 * </table>
 *
 * @author liman
 * @version 1.0.0
 * @since 1.0.0
 */
@Service
public class NotificationServiceImpl implements NotificationService {
	/**
	 * 静态变量：系统日志
	 */
	private static final Log logger = LogFactory.getLog(NotificationController.class);
	/**
	 * 获取配置的广告的发布时间的最大限制
	 */
	@Value("${publishTimeConfig.maxPublishTime}")
	private Integer maxPublishTime;
	/**
	 * 获取配置的广告的撤销时间的最大限制
	 */
	@Value("${revokeTimeConfig.maxRevokeTime}")
	private String maxRevokeTime;

	@Resource
	private NotificationMapper notificationMapper;

	@Resource
	private NotificationUserMapMapper notificationUserMapMapper;

	@Resource
	private AmqpTemplate amqpTemplate;

	@Resource
	private UserMapper userMapper;

	@Resource
	private AttachmentMapper attachmentMapper;

	/**
	 * pc端获取通知信息列表
	 * @param getNotificationListVO
	 *          接收参数类VO
	 * @return IPage<AnnouncementBO> 返回通知列表
	 */
	@Override
	public IPage<NotificationBO> getPcNotificationList(GetNotificationListVO getNotificationListVO) {
		// 封装分页参数
		Page<NotificationBO> page = new Page<>(getNotificationListVO.getCurrentPage(),getNotificationListVO.getPageSize());
		// 查询
		return notificationMapper.getPcNotificationList(page,getNotificationListVO);
	}

	/**
	 * (non-Javadoc)
	 * <p>
	 * <p>
	 * com.newtouch.bxzs.message.service.impl.NotificationServiceImpl#
	 * deleteNotification(java.util.List)
	 */
	@Override
	public void deleteNotification(List<String> notificationIdList) {
		if (notificationIdList.isEmpty()) {
			// 为空抛出异常信息
			throw new BaseException(BaseErrorEnum.REQUEST_DATA_NULL);
		}
		QueryWrapper<Notification> queryWrapper = new QueryWrapper<>();
		// 查询所有对应id的通知信息
		queryWrapper.in("NOTIFICATION_ID", notificationIdList);
		queryWrapper.eq("PUBLISH_STATUS", PublishStatusEnum.PUBLISHED_STATUS.getResultCode());
		List<Notification> notifications = notificationMapper.selectList(queryWrapper);
		// 有处于发布中的通知不能删除
		if (!notifications.isEmpty()) {
			throw new BaseException(BaseErrorEnum.PUBLISH_STATUS);
		}
		/* 批量逻辑删除通知 */
		if (notificationMapper.deleteBatchIds(notificationIdList) > 0) {
			/* 批量删除附件信息 */
			attachmentMapper.delete(new QueryWrapper<Attachment>().in("ATTACHMENT_RELATION_ID", notificationIdList));
		}
	}

	/**
	 * (non-Javadoc)
	 * <p>
	 * <p>
	 * com.newtouch.bxzs.message.service.impl.NotificationServiceImpl#
	 * modifyNotification(com.newtouch.bxzs.message.model.NotificationVO)
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public String modifyNotification(NotificationVO notificationVO) {
		/* 为空抛出异常 */
		if (BeanUtil.isEmpty(notificationVO)) {
				throw new BaseException(BaseErrorEnum.REQUEST_DATA_NULL);
		}
		/* 必填项：通知名称、通知导语、通知内容、通知类型、接收人*/
		if (StringUtils.isEmpty(notificationVO.getNotificationName())
				|| StringUtils.isEmpty(notificationVO.getNotificationIntroduction())
				|| StringUtils.isEmpty(notificationVO.getNotificationContent())
				|| StringUtils.isEmpty(notificationVO.getNotificationType())
				|| (notificationVO.getReceiveUserIdList() == null)|| notificationVO.getReceiveUserIdList().isEmpty()) {
			throw new BaseException(BaseErrorEnum.REQUEST_DATA_NULL);
		}

		Date date = new Date();
		// 校验发布时间和撤销时间
		verifyPublishTimeAndRevokeTime(notificationVO.getPublishTime(),notificationVO.getRevokeTime(),date);
		Notification notification = new Notification();
		/* 转换成实体存储类 */
		BeanUtil.copyProperties(notificationVO, notification);
		/*处理接收人 */
		List<String> receiveUserIdList = notificationVO.getReceiveUserIdList();
		notification.setReceiveUser(toJSONString(receiveUserIdList));


		/* 业务通知维护其子类型为6:业务提醒 */
		if (notificationVO.getNotificationType().equals(NotificationTypeEnum.NOTIFICATION_BUSINESS_TYPE.getResultCode())) {
			notification.setNotificationSubtype(BusinessMessageEnum.SERVICE_MESSAGE_REMINDER.getResultCode());
		}
		/* 判断是否立即发布 */
		if (notificationVO.getStoragePublishButton().equals(ButtonTypeEnum.PUBLIC_BUTTON.getCode())) {
			/* 设置发布时间为当前时间 */
			notification.setPublishTime(date);
			/* 设置发布状态为已发布 */
			notification.setPublishStatus(PublishStatusEnum.PUBLISHED_STATUS.getResultCode());
		} else {
			/* 设置发布状态为为未发布 */
			notification.setPublishStatus(PublishStatusEnum.UNPUBLISH_STATUS.getResultCode());
		}

		/* 新增通知 */
		if (StringUtils.isEmpty(notification.getNotificationId())) {
			/* 设置创建人和时间 */
			notification.setCreateUserId(Objects.requireNonNull(UserDetailUtil.getUserDetail()).getUserId());
			notification.setCreateTime(date);
			/* 新增日志 */
			logger.info("增加通知信息");
			/* 新增通知信息 */
			notificationMapper.insert(notification);
		} else {
			/* 修改通知 */
			Notification notification1 = notificationMapper.selectById(notification.getNotificationId());
			/* 修改信息不存在 */
			if (BeanUtil.isEmpty(notification1)) {
				throw new BaseException(BaseErrorEnum.NOT_FOUND);
			}
			/* 如果信息已发布将不能修改 */
			if (notification1.getPublishStatus().equals(PublishStatusEnum.PUBLISHED_STATUS.getResultCode())) {
				throw new BaseException(BaseErrorEnum.PUBLISH_STATUS);
			}
			/* 设置修改人 */
			notification.setUpdateUserId(Objects.requireNonNull(UserDetailUtil.getUserDetail()).getUserId());
			/* 设置修改时间 */
			notification.setUpdateTime(date);
			/* 修改日志 */
			logger.info("修改通知信息");
			/* 通过id修改通知信息 */
			notificationMapper.updateById(notification);
			// 删除用户消息中间表中原来的数据
			notificationMapper.deleteByNotificationId(notification.getNotificationId());
		}

		// 如果当前通知为发布状态，将接收人消息内容放入消息队列
		if(PublishStatusEnum.PUBLISHED_STATUS.getResultCode().equals(notification.getPublishStatus())){
			sendRabbitMqMessage(notification.getNotificationId(), notificationVO.getNotificationName(), notificationVO.getNotificationIntroduction(),receiveUserIdList);
		}
		return notification.getNotificationId();
	}

	/**
	 * (non-Javadoc)
	 * <p>
	 * <p>
	 * com.newtouch.bxzs.message.service.impl.NotificationServiceImpl#
	 * changeNotificationStatus(com.newtouch.bxzs.message.model.NotificationVO)
	 */
	@Override
	public void changeNotificationStatus(ChangeNotificationStatusVO vo) {
		// 参数校验
		Assert.notEmpty(vo,"参数不能为空");
		Assert.notEmpty(vo.getNotificationId(),"参数通知id不能为空");
		Assert.notEmpty(vo.getStoragePublishButton(),"参数发布/撤销类型不能为空");

		// 当前时间
		Date date = new Date();

		// 根据id查询
		Notification notification = notificationMapper.selectById(vo.getNotificationId());
		// 判断是否存在此信息
		if (BeanUtil.isEmpty(notification)) {
			// 抛出没有此通知信息
			throw new BaseException(BaseErrorEnum.NOT_FOUND.getResultMsg());
		}

		// 发布操作
		if (vo.getStoragePublishButton().equals(ButtonTypeEnum.PUBLIC_BUTTON.getCode())) {
			// 设置已发布状态
			notification.setPublishStatus(PublishStatusEnum.PUBLISHED_STATUS.getResultCode());
			// 设置发布时间为当前时间
			notification.setPublishTime(date);
			// 发布时需要通知给用户，调用消息队列,查询消息的接收人
			List<String> userIds = parseArray(notification.getReceiveUser(),String.class);
			// 发送到消息队列
			sendRabbitMqMessage(notification.getNotificationId(), notification.getNotificationName(), notification.getNotificationIntroduction(), userIds);
		}
		// 撤销时设置已撤销状态
		if (vo.getStoragePublishButton().equals(ButtonTypeEnum.REVOKE_BUTTON.getCode())) {
			// 设置撤销状态为已撤销
			notification.setPublishStatus(PublishStatusEnum.REVOKED_STATUS.getResultCode());
			// 设置当前时间为撤销时间
			notification.setRevokeTime(date);
		}
		// 设置修改人
		notification.setUpdateUserId(Objects.requireNonNull(UserDetailUtil.getUserDetail()).getUserId());
		// 设置修改时间
		notification.setUpdateTime(new Date());
		notificationMapper.updateById(notification);
	}

	/**
	 * 查询app端通知列表
	 * @param getNotificationListVO 参数
	 * @return  AppNotificationBO
	 */
	@Override
	public AppNotificationBO getAppNotificationList(GetNotificationListVO getNotificationListVO) {
		String userId = Objects.requireNonNull(UserDetailUtil.getUserDetail()).getUserId();
		AppNotificationBO appNotificationBO = new AppNotificationBO();
		// 查询每个类型的未读条数
		List<UnReadCountByType> unReadCountByTypeList = notificationMapper.countUnreadByType(userId);
		appNotificationBO.setUnreadCountList(unReadCountByTypeList);

		// 封装分页参数
		Page<AppNotificationListBO> page = new Page<>(getNotificationListVO.getCurrentPage(),getNotificationListVO.getPageSize());
		// 查询通知列表
		IPage<AppNotificationListBO> list = notificationMapper.selectNotificationList4App(page,userId,getNotificationListVO.getNotificationType());
		appNotificationBO.setNotificationList(list);
		return appNotificationBO;
	}

	/**
	 * 查询App端业务通知的类型
	 * @return AppServiceNotifyTypeListBO
	 */
	@Override
	public List<AppServiceNotifyTypeListBO> getAppServiceNotificationTypeList() {
		// 当前用户id
		String userid = Objects.requireNonNull(UserDetailUtil.getUserDetail()).getUserId();
		return notificationMapper.getAppServiceNotificationTypeList(userid);
	}

	/**
	 * 获取App端业务通知列表
	 * @param vo 查询参数
	 * @return IPage<AppNotificationBO>
	 */
	@Override
	public IPage<AppNotificationListBO> getAppServiceNotificationList(GetServiceNotificationListVO vo) {
		Assert.notEmpty(vo,"参数不能为空");
		Assert.notEmpty(vo.getNotificationSubtype(),"参数业务通知子类型不能为空");
		// 当前用户id
		String userId = Objects.requireNonNull(UserDetailUtil.getUserDetail()).getUserId();
		// 封装分页参数
		Page<AppNotificationListBO> page = new Page<>(vo.getCurrentPage(), vo.getPageSize());
		// 查询
		return notificationMapper.getAppServiceNotificationList(page,userId,vo.getNotificationSubtype());
	}

	/**
	 * PC端根据通知编号获取通知信息详情
	 * @param notificationId 通知id
	 * @return  详情
	 */
    @Override
    public NotificationDetailBO getPcNotificationDetail(String notificationId) {
        Assert.notEmpty(notificationId,"通知id不能为空");
		NotificationDetailBO pcNotificationDetail = notificationMapper.getPcNotificationDetail(notificationId);
		List<String> userIds = parseArray(pcNotificationDetail.getReceiveUser(), String.class);
		if(userIds!= null && !userIds.isEmpty()){
			pcNotificationDetail.setReceivePerson(userMapper.listByUserIdList(userIds));
		}else {
			pcNotificationDetail.setReceivePerson(notificationMapper.getNotificationReceiveUser(notificationId));
		}
		return pcNotificationDetail;
    }

	/**
	 * App端根据通知id获取通知信息详情
	 * @param notificationId 通知id
	 * @return AppNotificationDetailBO
	 */
    @Override
    public AppNotificationDetailBO getAppNotificationDetail(String notificationId) {
		Assert.notEmpty(notificationId,"通知id不能为空");
		AppNotificationDetailBO appNotificationDetail = notificationMapper.getAppNotificationDetail(notificationId);
		// 如果该消息已被撤销，则提示用户
		if(PublishStatusEnum.REVOKED_STATUS.getResultCode().equals(appNotificationDetail.getPublishStatus())){
			// 删除用户消息表中的该条记录
			notificationUserMapMapper.deleteByNotificationIdAndUserId(notificationId, Objects.requireNonNull(UserDetailUtil.getUserDetail()).getUserId());
			throw new BaseException(BaseErrorEnum.NOTIFICATION_IS_REVOKED);
		}
		return appNotificationDetail;
    }

	/**
	 * 将未读通知改为已读
	 * @param notificationId 通知id
	 */
	@Override
	public void modifyReadStatus(String notificationId) {
		// 当前用户id
		String userId = Objects.requireNonNull(UserDetailUtil.getUserDetail()).getUserId();
		notificationUserMapMapper.modifyReadStatus(notificationId,userId);
	}

	/**
	 * 处维护通知用户关系表
	 * @param notificationId 通知id
	 * @param userIds 接收用户id集合
	 */
	public void insertToNotificationUserMap(String notificationId,List<String> userIds){
    	List<NotificationUserMap> list = new ArrayList<>();
    	// 当前登录用户id
		String currUserId = Objects.requireNonNull(UserDetailUtil.getUserDetail()).getUserId();
		Date date = new Date();
		// 遍历接收用户
		for (String userId : userIds) {
			// 封装NotificationUserMap
			NotificationUserMap notificationUserMap = new NotificationUserMap();
			notificationUserMap.setNotificationId(notificationId);
			notificationUserMap.setUserId(userId);
			notificationUserMap.setReadFlag(false);
			notificationUserMap.setCreateUserId(currUserId);
			notificationUserMap.setCreateTime(date);
			notificationUserMap.setDeleteFlag(false);
			list.add(notificationUserMap);
		}
		// 批量插入
		notificationUserMapMapper.saveBatch(list);
	}

	/**
	 * 验证发布时间、撤销时间
	 * @param publishTime 发布时间
	 * @param revokeTime 撤销时间
	 * @param date 当前时间
	 */
	public void verifyPublishTimeAndRevokeTime(Date publishTime,Date revokeTime,Date date){
		/* 距离当前时间七天 */
		DateTime dateTimePublish = DateUtil.offsetDay(date, maxPublishTime);
		/* 撤销最大时间2050-01-01 00:00:00 */
		DateTime dateTimeMax = DateUtil.parseDateTime(maxRevokeTime);
		if (publishTime != null) {
			/* 判断发布时间是否在七天内 */
			if (publishTime.after(dateTimePublish) || publishTime.before(date)) {
				throw new BaseException(BaseErrorEnum.BODY_NOT_MATCH);
			}
			/* 撤销时间必须晚于发布时间 */
			if (revokeTime != null && revokeTime.before(publishTime)) {
				throw new BaseException(BaseErrorEnum.BODY_NOT_MATCH);
			}
		}
		/* 撤销时间不能大于2050-01-01 00:00:00 */
		if (revokeTime != null && revokeTime.after(dateTimeMax)) {
			throw new BaseException(BaseErrorEnum.BODY_NOT_MATCH);
		}
	}

	/**
	 * 发送rabbitMq
	 * @param notificationId 通知id
	 * @param notificationName 通知名称
	 * @param notificationContent 通知内容
	 * @param userIdList 接收人id
	 */
	public void sendRabbitMqMessage(String notificationId,String notificationName, String notificationContent, List<String> userIdList ){
		PushMessageBO message = PushMessageBO.builder()
				.title(notificationName)
				.content(notificationContent)
				.build();
		Map<String,Object> map = new HashMap<>(2);
		map.put(RabbitMQConstant.USER_ID_LIST_STR,userIdList);
		map.put(RabbitMQConstant.MESSAGE_STR,message);
		map.put(RabbitMQConstant.NOTIFICATION_ID_STR,notificationId);

		//消息持久化，默认都是持久化
		Message msg = MessageBuilder.withBody(JSON.toJSONString(map).getBytes(StandardCharsets.UTF_8))
				.setDeliveryMode(MessageDeliveryMode.PERSISTENT)
				.build();
		// 将消息放到消息队列
		amqpTemplate.convertAndSend(RabbitMQConstant.EXCHANGE_STR,RabbitMQConstant.PC_MESSAGE_NOTICE,msg);
	}
}